package com.freestyle.nettychat.session.service;

import com.freestyle.nettychat.chat.service.ChatService;
import com.freestyle.nettychat.session.dto.Session;
import com.freestyle.redis.expire.RedisKeyExpirationListener;
import org.springframework.data.redis.core.RedisTemplate;

import javax.annotation.Nullable;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.time.Duration;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by rocklee on 2022/2/4 12:20
 */
public class RedisSessionService implements SessionService  {
  private static final String KEY_LOGIN_LIST="NETTY_CHAT_LOGIN_LIST>";
  private static final String KEY_GROUP_USER_LIST="KEY_GROUP_USER_LIST";
  private RedisKeyExpirationListener redisKeyExpirationListener;
  private RedisTemplate<String,Object> redisTemplate;
  private ChatService chatService;

  public RedisSessionService(RedisTemplate<String, Object> redisTemplate, ChatService chatService,RedisKeyExpirationListener redisKeyExpirationListener) {
    this.redisTemplate = redisTemplate;
    this.chatService=chatService;
    this.redisKeyExpirationListener=redisKeyExpirationListener;
    redisKeyExpirationListener.addProcess(msg->{
      String rawStr=null;
      //byte[] content=null;
      try(ByteArrayInputStream bis=new ByteArrayInputStream(msg.getBody());
          ObjectInputStream ois=new ObjectInputStream(bis)
      ){
        rawStr= (String) ois.readObject();
      } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
      }
      if (rawStr.startsWith(KEY_LOGIN_LIST)){
        String uid=rawStr.split(">")[1];
        redisTemplate.opsForList().remove(KEY_LOGIN_LIST,1, uid);
      }
    });
    chatService.setSessionService(this);
  }
  private String getRedisUidKey(String uid){
    return KEY_LOGIN_LIST+uid;
  }
  @Override
  public Session login(String uid, String password) {
    String redisKey=getRedisUidKey(uid);
    Session session=(Session)redisTemplate.opsForValue().get(redisKey);
    if (session==null) {
      session = new Session();
    }
    session.setUid(uid);
    session.setLoginTime(new Date());
    //将login session扔到redis
    updateSession(session);
    if (redisTemplate.opsForList().indexOf(KEY_LOGIN_LIST,uid)==null) {
      redisTemplate.opsForList().leftPush(KEY_LOGIN_LIST, uid);
    }
    return session;
  }

  @Override
  public Session getSession(String uid) {
    return (Session) redisTemplate.opsForValue().get(getRedisUidKey(uid));
  }

  @Override
  public void updateSession(Session session) {
    String key=getRedisUidKey(session.getUid());
    redisTemplate.opsForValue().set(key,session, Duration.ofHours(2));
  }

  @Override
  public void grantChatRoom(String uid, String chatRoom) {
    Set<String> members= (Set<String>) redisTemplate.opsForHash().get(KEY_GROUP_USER_LIST,chatRoom);
    if (members==null){
      throw new IllegalStateException("聊天室不存在:"+chatRoom);
    }
    if (!members.contains(uid)){
      members.add(uid);
      redisTemplate.opsForHash().put(KEY_GROUP_USER_LIST,chatRoom,members);
    }
  }

  @Override
  public void logout(String uid) {
    Session session=getSession(uid);
    if (session==null)return;
    try {
      List<String> chatRooms= getOnLineChatRoom().keySet().stream().filter(s->
        ((Set<String>)redisTemplate.opsForHash().get(KEY_GROUP_USER_LIST,s)).contains(uid)
      ).collect(Collectors.toList());
      for (String chatRoom : chatRooms) {
        removeFromChatRoom(session,chatRoom);
      }
      String redisKey=getRedisUidKey(uid);
      redisTemplate.delete(redisKey);
      redisTemplate.opsForList().remove(KEY_LOGIN_LIST,1,uid);

    } catch (Exception e) {
      e.printStackTrace();
    }


  }

  @Override
  public boolean removeFromChatRoom(Session session, String chatRoom) {
    if (session==null)return false;
    Set<String> members= (Set<String>) redisTemplate.opsForHash().get(KEY_GROUP_USER_LIST,chatRoom);
    if (members==null){
      throw new IllegalStateException("聊天室不存在:"+chatRoom);
    }
    if (members.contains(session.getUid())){
      members.remove(session.getUid());
      redisTemplate.opsForHash().put(KEY_GROUP_USER_LIST,chatRoom,members);
    }
    return true;
  }

  @Override
  public List<Session> getOnLineUserList(@Nullable String chatRoom) {
    List<Session> sessions=new LinkedList<>();
    if(chatRoom==null){
      List<Object> lst=redisTemplate.opsForList().range(KEY_LOGIN_LIST,0,-1);
      lst.forEach(o->{
        Session session=getSession((String)o);
        if (session!=null) {
          sessions.add(getSession((String) o));
        }
      });
    }
    else{
      ((Set<String>)redisTemplate.opsForHash().get(KEY_GROUP_USER_LIST,chatRoom))
              .stream().sorted().forEach(o->{
                if (!((String)o).startsWith("*")) {
                  Session session=getSession((String)o);
                  if (session!=null) {
                    sessions.add(getSession((String) o));
                  }
                }
      });
    }
    return sessions;
  }

  @Override
  public Map<String, String> getOnLineChatRoom() {
    Map<String,String> onLineChatRooms=new LinkedHashMap<>();
    redisTemplate.opsForHash().keys(KEY_GROUP_USER_LIST).stream().sorted().forEach(room->{
      Set<String> members= (Set<String>) redisTemplate.opsForHash().get(KEY_GROUP_USER_LIST,room);
      onLineChatRooms.put((String) room, members.stream().filter(i->i.startsWith("*")).findAny().orElse(""));
    });
    return onLineChatRooms;
  }

  @Override
  public void createChatRoom(String chatRoom, String owner) {
      if (redisTemplate.opsForHash().hasKey(KEY_GROUP_USER_LIST,chatRoom)){
        throw new RuntimeException("聊天室已存在");
      }
      Set<String> members=new HashSet<>();
      members.add("*"+owner);
      members.add(owner);
      redisTemplate.opsForHash().put(KEY_GROUP_USER_LIST,chatRoom,members);
  }

  @Override
  public void removeChatRoom(String chatRoom) {
    redisTemplate.opsForHash().delete(KEY_GROUP_USER_LIST,chatRoom);//删除该室的成员数据
    chatService.removeChatRoomLog(chatRoom);
  }

}
